<template>
  <div class="fa-sign-select fa-contract-scope" :class="`is-${typeData.type}`">
    <header class="fa-widget-popover__hd">
      全部{{ label }}({{ total }})

      <ElCheckbox v-if="signOptions" v-model="signOptions.signGlobal">应用于全局</ElCheckbox>
    </header>

    <ElInput
      v-model="search"
      class="fa-sign-select__search"
      :placeholder="`请输入${label}名称搜索`"
      clearable
      size="mini"
      @blur="search = search.trim()"
    />

    <ElEmpty v-if="!list.length" :message="emptyText" size="80" />

    <ul v-else ref="scroller" class="fa-sign-select__bd">
      <SignSelectItem
        v-for="item in list"
        :key="item.id"
        :class="[signClassName && signClassName(item)]"
        :item="item"
        :type="typeData.type"
        v-bind="getState(item)"
        :tag="getTag(item)"
        @select="select"
        @drag="drag"
      />
    </ul>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, PropType, ref } from 'vue'
import { Viewer, SignType, SignData, SignState, WidgetType, TagProps } from '@/types'
import { Widget } from '@/models'
import SignSelectItem from './SignSelectItem.vue'

const TYPES = {
  seal: {
    type: SignType.SEAL,
    name: '印章'
  },

  signature: {
    type: SignType.SIGNATURE,
    name: '签名'
  }
}

/** 签章选择 */
export default defineComponent({
  name: 'FaSignSelect',

  components: { SignSelectItem },

  props: {
    /**
     * 类型
     * 可选值：seal 印章（默认）；signature 签名
     */
    type: {
      type: [String, Number] as PropType<SignType | string>,
      default: SignType.SEAL
    },

    /** 印章、签名列表 */
    data: {
      type: Array as PropType<SignData[]>,
      default: () => []
    },

    /** 禁用 */
    disable: Function as PropType<(sign: SignData) => boolean>,

    /** 是否允许拖拽 */
    draggable: {
      type: Boolean,
      default: false
    },

    /** 签章自定义样式 */
    signClassName: Function as PropType<(sign: SignData) => string | void>,

    /** 文档查看组件 */
    viewer: {
      type: Object as PropType<Viewer | null>
    },

    /** 当前操作控件 */
    widget: {
      type: Object as PropType<Widget>
    }
  },

  emits: {
    /** 选择 */
    select: (item: SignData) => void 0,

    /** 拖拽 */
    drag: (e: DragEvent, item: SignData) => void 0
  },

  setup(props, { emit }) {
    /** 搜索值 */
    const search = ref('')

    /** 过滤列表 */
    const list = computed(() => {
      // 指定印章数组
      let assigned = props.data.filter(i => {
        return isAssigned(i)
      })

      // 非指定印章数组
      let unassigned = props.data.filter(i => {
        return !isAssigned(i)
      })

      let data = [...assigned, ...unassigned]

      const src = search.value.trim()

      /** 搜索 */
      if (src) {
        data = data.filter(item => item.name?.includes(src))
      }

      return data
    })

    /** 总数 */
    const total = computed(() => props.data.length)

    /** 无数据 */
    const emptyText = computed(() => (search.value ? '无搜索结果' : `暂无${label.value}`))

    /** 类型数据 */
    const typeData = computed(
      () => TYPES[props.type as 'seal' | 'signature'] || Object.values(TYPES).find(item => item.type === props.type)
    )

    /** 标签展示 */
    const label = computed(() => typeData.value.name)

    /** 已选择印章 */
    const selected = ref(props.widget?.sign || null)

    /** 当前签章是否禁用 */
    function isDisabled(item: SignData) {
      // 配置禁用
      if (item.disabled || props.disable?.(item)) return true

      // 当前操作控件
      const { widget } = props

      // 指定位置——单个控件（当前印章和控件指定印章不匹配，则禁用）
      if (widget) {
        return widget.assignedSignId && widget.assignedSignId !== item.id
      }

      // 所有指定位置控件
      const fixedWidgets = props.viewer?.widgets.filter(
        i => i.isUse && i.fixed && i.type === (props.type === SignType.SEAL ? WidgetType.SEAL : WidgetType.SIGNATURE)
      )

      // 指定位置签署
      if (fixedWidgets?.length) {
        // 当前印章可用（存在未指定或者指定当前印章的控件，则可用）
        const canUse = fixedWidgets.some(i => !i.assignedSignId || i.assignedSignId === item.id)

        return !canUse
      }

      // 非指定位置签署
      return false
    }

    /** 当前签章是否指定 */
    function isAssigned(sign: SignData) {
      const { widget, viewer } = props

      // 单个目标控件弹窗
      if (widget?.assignedSignId) {
        return widget.assignedSignId === sign.id
      }

      // 全局控件弹窗
      let targetWidget = viewer?.widgets.find(i => {
        return i.assignedSignId === sign.id
      })

      return !!targetWidget
    }

    /** 获取状态 */
    function getState(item: SignData): SignState {
      return {
        disabled: !!isDisabled(item),
        draggable: props.draggable,
        selected: selected.value === item,
        assigned: !!isAssigned(item)
      }
    }

    /** 默认标签 */
    function getDefaultTag(item: SignData, state: SignState) {
      if (state.assigned) return '指定'

      if (item.default) return '默认'
    }

    /** 获取标签 */
    function getTag(item: SignData): TagProps | undefined {
      const { viewer } = props

      const state = getState(item)

      let tag =
        viewer?.signOptions.signSelectTag?.(item, typeData.value.type, state, props.widget) ||
        getDefaultTag(item, state) ||
        item.tag

      if (typeof tag === 'string') return { text: tag }

      return tag
    }

    return {
      /** 搜索值 */
      search,

      /** 过滤列表 */
      list,

      /** 总数 */
      total,

      /** 标签展示 */
      label,

      /** 无数据 */
      emptyText,

      typeData,

      /** 签章配置选项 */
      signOptions: computed(() => props.viewer?.signOptions),

      /** 获取标签 */
      getTag,

      /** 获取状态 */
      getState,

      /** 选择印章 */
      select(item: SignData) {
        if (props.draggable) return

        selected.value = item
        emit('select', item)
      },

      /** 拖拽印章 */
      drag(e: DragEvent, item: SignData) {
        emit('drag', e, item)
      }
    }
  }
})
</script>

<style lang="scss">
/** 尺寸 */
$s: 140px;

.fa-sign-select {
  font-size: 12px;

  &__bd {
    display: grid;
    grid-template-columns: repeat(auto-fit, $s);
    gap: 8px;
    justify-content: space-evenly;
    max-height: 500px;
    max-height: 80vh;
    margin: 8px 0 0 0;
    padding: 0;
    overflow: auto;
    list-style: none;
  }

  .el-empty {
    &__image {
      width: 128px;
    }

    &__description p {
      font-size: 12px;
    }
  }
}
</style>
